package com.lmb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lmb.bean.*;
import com.lmb.dao.CategoryDao;
import com.lmb.dao.DishDao;
import com.lmb.dao.DishFlavorDao;
import com.lmb.dto.DishDto;
import com.lmb.exception.CustomException;
import com.lmb.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.sql.Wrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

@Service
public class DishServiceImpl extends ServiceImpl<DishDao, Dish> implements DishService {
    @Autowired
    private DishDao dishDao;

    @Autowired
    private DishFlavorDao dishFlavorDao;

    @Autowired
    private CategoryDao categoryDao;

    /**
     * 新增菜品：
     *  注意：由于新增菜品操作了两张表  进行了2个写操作，要保证插入菜品基本信息和口味信息同时成功 新增菜品才成功 所以需要添加事务管理
     *  实现：1.在方法上打上@Transactional注解  2.在启动类上开启事务注解驱动管理
     * @param dishDto
     */
    @Transactional
    @Override
    public void saveWithFlavors(DishDto dishDto) {
        //0.检查菜品名称不能重复
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dish::getName,dishDto.getName());
        Integer count = dishDao.selectCount(wrapper);
        if (count>0){
            throw new CustomException(0,"菜品名称'"+dishDto.getName()+"'已经存在");
        }

        //1.保存菜品基本信息到菜品口味表dish
        //可以直接传dishDto对象 因为dishDto继承了Dish  插入时只会根据Dish类中有的属性设置插入字段的值 多余的字段会自动忽略
        dishDao.insert(dishDto);

        //获取菜品id  MyBatisPlus中当完成了新增操作之后，会自动的将生成的id赋值到对象的主键属性上
        Long dishDtoId = dishDto.getId();

        //2.保存菜品口味信息到菜品口味表dish_flavor
        //2.1：获取菜品口味信息列表
        List<DishFlavor> flavors = dishDto.getFlavors();

        //2.2：为当前的菜品口味设置关联的菜品id dish_id字段
        //传统方式：比较low
        /*if(flavors!=null||flavors.size()>0){
            for (DishFlavor flavor : flavors) {
                flavor.setDishId(dishId);
                //2.3:保存口味信息到口味表中
                dishFlavorDao.insert(flavor);
            }
        }*/
        /*
            Lambda表达式：
                1.写法格式：()->{}
                2.如果只有一个参数 ()可以省略
                3.如果大括号中只有一句代码，并且方法没有返回值 {}也可以省略
         */
        //高级玩法：Stream流操作
        flavors.stream().forEach(flavor->{
            flavor.setDishId(dishDtoId);
            dishFlavorDao.insert(flavor);
        });

    }

    /**
     * 分页查询：
     *  问题1：分页查询时 菜品图片没有显示
     *      原因：没有将测试数据中 菜品对应图片放入 D:\reggie\目录下
     *      解决：将资源中提供的图片资源放入D:\reggie\目录下
     *  问题2：分页查询时  菜品所属的分类名称没有展示
     *      原因：前端页面进行菜品所属分类展示时 需要的是菜品分类名称categoryName 而我们查询菜品表返回的数据只有菜品所属分类id 没有分类名称
     *      解决：分页查询返回菜品列表数据的同时  在每一条菜品数据中应该包含菜品的所属分类名称categoryName
     *      将每一个菜品dish对象中的数据 封装到dishDto对象中，并且设置dishDto对象的categoryName属性即可实现前端的正常展示
     *      响应的数据  Page<Dish>>   ==》     Page<DishDto>>
     *      原来响应的给前端的数据是        R<Page<Dish>> ----->    Page<Dish>：封装分页相关的数据    records：当前页数据List<Dish>     total：总条数
     *      现在我们要响应给前端的数据是     R<Page<DishDto>> ----> Page<DishDto>:封装分页相关的数据  records：当前页数据 List<DishDto>  total：总条数
     *      实际实现步骤：
     *          1.从dishPage对象中获取当前页数据列表 dishList  List<Dish>
     *          2.遍历dishList集合 将集合中的每一个dish对象中的数据赋值到DishDto对象中，并且为DishDto对象的categoryName属性赋值
     *          3.在遍历dishList集合时，将每一个新创建出来的dishDto对象 存入到dishDtoList集合中 【这样就得到当前页要显示的数据集合了】
     *          4.将当前页数据集合dishDtoList和总条数封装到dishDtoPage对象中返回即可
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @Override
    public Page<DishDto> findPage(Integer page, Integer pageSize, String name) {
        //0.配置分页查询拦截器	【已经配置好了】
        //1.封装分页参数page和pageSize到Page对象中
        Page<Dish> dishPage = new Page<>(page, pageSize);

        //2.设置分页查询条件 根据菜品名称name进行模糊查询
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(name!=null&&name.length()>0,Dish::getName,name);

        //3.调用selectPage方法 传入Page对象和wrapper对象完成分页查询
        dishDao.selectPage(dishPage,wrapper);

        //完善代码：响应Page<DishDto>
        //a：创建Page<DishDto> dishDtoPage对象
        Page<DishDto> dishDtoPage = new Page<>();

        //b.将dishPage中的当前页数据集合List和总条数设置到dishDtoPage对象
        //当前页数据列表List
        List<Dish> dishList = dishPage.getRecords();

        //b1:将dishList中的每一个元素dish对象转为DishDto 存入到List<DishDto> dishDtoList中
        List<DishDto> dishDtoList = new ArrayList<>();

        for (Dish dish : dishList) {
            //创建DishDto对象
            DishDto dishDto = new DishDto();

            //将dish对象中的菜品信息赋值到dishDto对象中,要注意源对象的属性和目标对象的属性要一致
            BeanUtils.copyProperties(dish,dishDto);

            //查询当前菜品所属的分类名称
            Category category = categoryDao.selectById(dish.getCategoryId());
            dishDto.setCategoryName(category.getName());

            //将dishDto对象放入到DishDtoList集合中
            dishDtoList.add(dishDto);

        }

        //将当前页要显示的数据 设置到dishDtoPage对象的records属性上
        dishDtoPage.setRecords(dishDtoList);

        //b2:将总条数设置到dishDtoPage对象的total属性上
        dishDtoPage.setTotal(dishPage.getTotal());

        //4.返回Page对象【Page对象中包含当前页数据集合List和总条数】
        return dishDtoPage;
    }

    //根据id查询菜品信息
    @Override
    public DishDto getByIdWithFlavors(Long id) {
        //1.根据菜品id查询菜品的基本信息数据
        Dish dish = dishDao.selectById(id);

        //2.根据菜品id查询菜品的口味信息数据
        LambdaQueryWrapper<DishFlavor> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DishFlavor::getId,id);
        List<DishFlavor> flavors = dishFlavorDao.selectList(wrapper);

        //3.将菜品的基本信息及其口味信息封装到DishDto对象中返回
        DishDto dishDto = new DishDto();
        //使用Spring提供的BeanUtils工具类 可以将一个源对象的属性数据赋值到一个目标对象属性上 要求属性名称前后一致
        BeanUtils.copyProperties(dish,dishDto);
        dishDto.setFlavors(flavors);

        return dishDto;
    }

    /**
     * 菜品修改
     * @param dishDto
     */
    @Transactional
    @Override
    public void updateWithFlavors(DishDto dishDto) {
        //1.判断菜品修改后要保存的菜品名称不能和其他的菜品名称相同
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dish::getName,dishDto.getName());
        wrapper.ne(Dish::getId,dishDto.getId());
        Integer count = dishDao.selectCount(wrapper);
        if (count>0){
            throw new CustomException(0,"菜品名称'"+dishDto.getName()+"'已存在！");
        }

        //2.修改菜品dish表中的菜品基本信息
        dishDao.updateById(dishDto);

        //3.修改菜品口味dish_flavor表中的菜品口味信息
        //获取当前菜品id
        Long dishId = dishDto.getId();

        //3.1：删除当前菜品对应的所有口味信息   根据菜品口味对应的菜品id进行删除
        LambdaQueryWrapper<DishFlavor> flavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
        flavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
        dishFlavorDao.delete(flavorLambdaQueryWrapper);

        //3.2：将当前菜品修改后的口味信息保存到dish_flavor表中
        //a:获取当前菜品修改后的口味信息列表
        List<DishFlavor> flavors = dishDto.getFlavors();

        //b：循环遍历 要设置口味的id置为null，口味关联的dish_id要设置为当前菜品id
        flavors.stream().forEach(flavor->{
            //口味的id置为null
            flavor.setId(null);
            //口味关联当前菜品dish_id
            flavor.setDishId(dishId);
            //保存到dish_flavor表中
            dishFlavorDao.insert(flavor);
        });


    }

    //删除
    @Transactional
    @Override
    public void deleteByIds(Long[] ids) {
        //1.判断ids中是否为null  提示：没有选中要删除的套餐
        if (ids==null||ids.length==0){
            throw new CustomException(0,"没有选中要删除的套餐");
        }

        //2.判断要删除的套餐是否处于停售状态  如果处于起售状态：则不能删除 抛出异常提示
        //根据要删除的套餐id  设置查询条件status=1 进行统计 如果个数大于1，说明存在启售状态的套餐，不能删除
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        List<Long> idsList = Arrays.asList(ids);
        wrapper.in(Dish::getId,idsList);
        List<Dish> dishList = this.list(wrapper);
        dishList.stream().forEach(dish -> {
            if (dish.getStatus()==1){
                throw new CustomException(0,"当前菜品'"+dish.getName()+"'处于启售状态，无法删除！");
            }
            Long dishId = dish.getId();
            LambdaQueryWrapper<DishFlavor> dishFlavorWrapper = new LambdaQueryWrapper<>();
            dishFlavorWrapper.eq(DishFlavor::getDishId,dishId);
            dishFlavorDao.delete(dishFlavorWrapper);

            this.removeById(dish.getId());
        });

    }

    //批量启售禁售
    @Override
    public void updateDishStatus(Integer status, List<Long> ids) {
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ids!=null,Dish::getId,ids);
        List<Dish> lists = this.list(wrapper);
        lists.stream().forEach(list->{
            if (list!=null){
                list.setStatus(status);
                this.updateById(list);
            }
        });
    }



}
